import DropBox from './drop-box'
import AddScore from './add-score'
import SourceLoader from './sourceLoder'
import addIcon from './redpacket-icon-add.png'
import redIcon from './redpacket-icon.png'
import iNoBounce from './nobounce'

export const Loader = SourceLoader
export interface StageOpt {
  // 画布宽
  width: number
  // 画布高
  height: number
  // canvas 标签
  target: HTMLCanvasElement
  // 游戏时间
  gameTime: number
  // 红包图片
  images?: any[]
  // 是否使用音频
  isUseAudio?: boolean
  // 红包大小
  size?: number
}

interface CallbackOpt {
  // 游戏结束回调
  gameOver: Array<Function>
  // 点中红包回调
  clickBox: Array<Function>
  // 时间倒计时回调
  coundownTime: Array<Function>
}
type eventName = 'clickBox' | 'coundownTime' | 'gameOver'
export default class Stage {
  private width: number
  private height: number
  private ctx: CanvasRenderingContext2D | null
  private timer: number = 0
  private pool: Array<any>
  private _started: boolean = false
  private _time: number = 0
  private intervalTimer: any = 0
  private createBoxInterval: number = 1000
  private gameTime: number
  private images: any[]
  private tapAudio: any
  private isTouchStart: boolean = false
  private addIcon: any
  private events: CallbackOpt = { gameOver: [], coundownTime: [], clickBox: [] }
  private isUseAudio: boolean = false
  private iNoBounce: any
  private size: any
  constructor(opt: StageOpt) {
    this.iNoBounce = iNoBounce()
    this.isUseAudio = opt.isUseAudio || false
    this.width = opt.width || window.screen.width
    this.height = opt.height || window.screen.height
    this.size = opt.size || 80
    this.ctx = this.getCtx(opt.target)
    if (this.ctx) {
      this.ctx.fillStyle = 'rgba(255, 255, 255, 0)'
    }
    this.gameTime = opt.gameTime || 30
    const red = new Image()
    red.src = redIcon
    this.images = opt.images || [red]
    this.pool = []
    this.addIcon = new Image()
    this.addIcon.src = addIcon
    // 使用音频
    if (this.isUseAudio) {
      const source = new SourceLoader({
        list: [
          {
            src: 'https://jtmm.s3.cn-north-1.amazonaws.com.cn/h5/newjtmm/activityImg/tap.mp3',
            id: 'tapAudio',
          },
        ],
      })
      source.start()
      source.on('complate', () => {
        this.tapAudio = source.getSource('tapAudio')
        if (this.isTouchStart) {
          this.startGame()
          this.isTouchStart = false
        }
      })
    }
  }

  // 获取canvas 2d
  getCtx(canvas: HTMLCanvasElement): CanvasRenderingContext2D | null {
    canvas.addEventListener('click', this.onClick.bind(this))
    canvas.width = this.width
    canvas.height = this.height
    return canvas.getContext('2d')
  }
  /**
   * 事件监听
   * gameOver 游戏结束回调
   * clickBox 点击回调
   * coundownTime 倒计时监听
   * */
  on(name: eventName, fn: Function) {
    this.events[name].push(fn)
  }
  // 开始游戏
  startGame() {
    // 检查音频是否加载完毕
    if (this.isUseAudio && this.tapAudio) {
      this.isTouchStart = true
      return
    }
    this._started = true

    this.upData()
    this.intervalTimer = setInterval(() => {
      if (this.gameTime < 1) {
        // 停止创建
        this._started = false
        clearInterval(this.intervalTimer)
        // 回调结束
        this.events.gameOver.forEach((e: Function) => e && e())
      } else {
        this.gameTime--
        this.events.coundownTime.forEach((e: Function) => e && e(this.gameTime))
      }
    }, 1000)
  }

  // 更新ui
  upData() {
    // 清除画布
    if (this.ctx) this.ctx.clearRect(0, 0, this.width, this.height)
    // 创建红包
    this.creatDropBox()
    const arr: Array<any> = []
    // 过滤活跃对象
    this.pool.forEach((v: any) => {
      if (!v.isDestory) {
        // 更新每个对象
        v.updata()
        // 绘制图片
        if (this.ctx) {
          this.ctx.drawImage(v.img, v.x, v.y, v.w, v.h)
        }
        arr.push(v)
      }
    })

    // 赋值未回收的对象
    this.pool = [...arr]
    this.timer && window.cancelAnimationFrame(this.timer)
    this.timer = window.requestAnimationFrame(this.upData.bind(this))
  }

  isIntersect(point: any, rain: any): boolean {
    // 判断点击位置  是否处于某个rain之中
    const distanceX = point.x - rain.x
    const distanceY = point.y - rain.y
    const withinX = distanceX > 0 && distanceX < rain.w
    //  计算y
    const withinY = distanceY > 0 && distanceY < rain.y
    return withinX && withinY
  }

  // 点击
  onClick(e: any) {
    e.preventDefault && e.preventDefault()
    // 点击位置
    const pos = {
      x: e.clientX,
      y: e.clientY,
    }
    // 点中对象
    let maxIndex = -1
    let creatTime = 0
    this.pool.forEach((e: any, i: number) => {
      if (this.isIntersect(pos, e)) {
        if (e.creatTime >= creatTime) {
          creatTime = e.creatTime
          maxIndex = i
        }
      }
    })
    // 操作点击对象
    if (maxIndex > -1) {
      // 播放音频
      if (this.isUseAudio && this.tapAudio) {
        this.tapAudio.load()
        this.tapAudio.volume = 1
        this.tapAudio.play()
      }
      const target = { ...this.pool[maxIndex], gameTime: this.gameTime }
      // 回调监听点击事件
      this.events.clickBox.forEach((e: Function) => e(target))
      // 回收box
      this.pool.splice(maxIndex, 1)
      // 添加+1
      this.pool.push(
        new AddScore({ x: target.x, y: target.y, img: this.addIcon })
      )
    }
  }

  // 创建红包
  creatDropBox() {
    // 每间隔一段时间创建一个盒子
    const now = Date.now()
    if (now - this._time > this.createBoxInterval && this._started) {
      this._time = now
      const randomNum = this.randomNum(1, 4)
      // 增加难度
      if (this.gameTime < 20) {
        this.createBoxInterval -= 20
      }
      for (let i = 0; i < randomNum; i++) {
        const index =
          this.images.length > 1 ? this.randomNum(this.images.length - 1, 0) : 0
        this.pool.push(
          new DropBox({
            w: this.size ,
            h: this.size ,
            x: this.randomNum(0, this.width - 80),
            y: -this.randomNum(80, this.width / 2),
            img: this.images[index],
            v: Math.random() / 10,
          })
        )
      }
    }
  }

  /**
   * 生成随机数
   * @param maxNum
   * @param minNum
   */
  randomNum(maxNum: number, minNum: number = 0): number {
    return parseInt(Math.random() * (maxNum - minNum + 1) + minNum + '', 10)
  }

  // 销毁
  destory() {
    // 清除requestAnimationFrame
    this.timer && window.cancelAnimationFrame(this.timer)
    document.removeEventListener('click', this.onClick.bind(this))
    this.pool.forEach((e: any) => {
      e.destory && e.destory()
    })
    // 清除所有类
    this.pool = []
    // 清除滑动
    if (this.iNoBounce.isEnabled()) this.iNoBounce.disable()
    this.iNoBounce = null
    this.intervalTimer && clearInterval(this.intervalTimer)
    this.images = []
    this.ctx = null
    this.addIcon = null
    for (const item in this.events) {
      this.events[item as eventName] = []
    }
  }
}
